/** @file         server.c
 *  @brief        简要说明
 *  @details      详细说明
 *  @author       lzm
 *  @date         2021-06-10 09:49:46
 *  @version      v1.0
 *  @copyright    Copyright By lizhuming, All Rights Reserved
 *
 **********************************************************
 *  @LOG 修改日志:
 **********************************************************
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

// 注意: 接收缓冲区对应对方的发送缓冲区小于等于就不会出现对方接收多次。

#define CLIENT_BUFF_MAX 1024 // 客户端接收缓冲区 size
#define MY_SERVER_PORT 8080 // 服务端监听端口
#define CLIENT_PENDING_NUM 10 // 内核未完成队列的客户端缓冲数


/**
 * @name   client_fun
 * @brief  线程函数，处理 client
 * @param  arg：资源
 * @retval 
 * @author lzm
 */
void *client_fun(void *arg)
{
	int recv_len = 0; // 接收长度
	char recv_buff[CLIENT_BUFF_MAX] = "";
	long connfd = (long)arg; // 已连接socket。 值传递（地址传递时注意多线程并发问题）

	// [1] 接收数据
	while((recv_len = recv(connfd, recv_buff, sizeof(recv_buff), 0)) > 0)
	{
		printf("[%ld]recv_buff:%s\r\n", connfd, recv_buff); // 打印数据
		send(connfd, "OK", 2, 0); // 返回 client
	}

	// [2] 关闭 socket
	printf("client closed!\r\n");
	close(connfd); // 关闭 socket
	return NULL;
}



/**
 * @name   main
 * @brief  main函数，服务端处理
 * @param  
 * @retval 
 * @author lzm
 */
int main(int argc, char *argv[])
{
	int sockfd = 0; // 监听 socket
	long connfd = 0; // 已连接 socket 。long 避免使用 64bit 机器而出错
	int ret = 0; // 返回缓冲
	struct sockaddr_in local_server_addr; // 本地服务器地址
	unsigned short local_server_port = MY_SERVER_PORT; // 本地服务器监听端口
	pthread_t thread_client_id; // client 线程 id

	printf("TCP server started at port [%d]!\n", local_server_port);

	// [1] 创建套接字
	printf("create server socket!\n");
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		printf("%s-%s-%d:sockfd create faild!", __FILE__, __FUNCTION__, __LINE__);
		perror("socket create error");
		exit(-1);
	}

	// [2] 初始化地址数据
	printf("init server address!\n");
	bzero(&local_server_addr, sizeof(local_server_addr)); // 初始化服务器地址
	local_server_addr.sin_family = AF_INET;
	local_server_addr.sin_port = htons(local_server_port);
	local_server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	// [3] 绑定
	printf("bing server socket and addr!\n");
	ret = bind(sockfd, (struct sockaddr*)&local_server_addr, sizeof(local_server_addr));
	if(ret != 0)
	{
		printf("%s-%s-%d:sockfd bind faild!", __FILE__, __FUNCTION__, __LINE__);
		perror("socket bind error");
		close(sockfd);
		exit(-1);
	}

	// [4] 监听
	printf("listen server socket!\n");
	ret = listen(sockfd, CLIENT_PENDING_NUM);
	if(ret != 0)
	{
		printf("%s-%s-%d:sockfd listen faild!", __FILE__, __FUNCTION__, __LINE__);
		perror("socket listen error");
		close(sockfd);
		exit(-1);
	}

	// [5] 处理 client
	while(1)
	{
		char client_ip[INET_ADDRSTRLEN] = ""; // use for save client ip
		struct sockaddr_in client_addr; // use for save client address
		socklen_t client_len = sizeof(client_addr); // 必须初始化

		// [5][1] 获取一个已建立的连接
		printf("accept!\n");
		connfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);
		if(connfd < 0)
		{
			printf("%s-%s-%d:sockfd accept faild!", __FILE__, __FUNCTION__, __LINE__);
			perror("accept error");
			continue;
		}

		// [5][2] 处理客户端数据
		inet_ntop(AF_PACKET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
		printf("clien ip = [%s], port = [%d]\n", client_ip, ntohs(client_addr.sin_port));

		if(connfd > 0)
		{
			// 线程处理客户端数据
			pthread_create(&thread_client_id, NULL, (void *)client_fun, (void *)connfd); // creat thread。注意 64bit 机器地址是8byte的
			pthread_detach(thread_client_id); // thread 分离。即时，线程回调函数结束时自动回收该线程资源
		}
	}
	
	// [6] 关闭 server socket
	close(sockfd);

	return 0;
}



